#include "common.h"
#include <stdlib.h>
#include "burst.h"
#include "cpu/reg.h"

#define CACHE_BLOCK_SIZE 64  //定义cache block 存储空间的大小为 64B
#define CACHE_SIZE_L1 64*1024 //一级缓存大小为 64KB
#define CACHE_SIZE_L2 4*1024*1024 //二级缓存大小为 4MB
#define EIGHT_WAY 8 //8-way set associative
#define SIXTEEN_WAY 16 //16-way set associative
uint32_t dram_read(hwaddr_t, size_t);
void dram_write(hwaddr_t, size_t, uint32_t);
void ddr3_read(hwaddr_t,void*);
void ddr3_write(hwaddr_t,void*,uint8_t*);
int is_mmio(hwaddr_t);
void init_cache();
uint32_t cache_read(hwaddr_t addr);
uint32_t second_cache_read(hwaddr_t addr);
void cache_write(hwaddr_t addr,size_t len,uint32_t data);
void second_cache_write(hwaddr_t addr,size_t len,uint32_t data);
uint32_t mmio_read(hwaddr_t, size_t, int);
void mmio_write(hwaddr_t, size_t, uint32_t, int);
void call_ddr3_read(hwaddr_t addr, void *data);
void call_ddr3_write(hwaddr_t, void *, uint8_t *);

int tol_time;
extern uint8_t current_sreg;
/* Memory accessing interfaces */
struct Cache{
    bool valid; //标志位
    int tag; //tag位
    uint8_t data[CACHE_BLOCK_SIZE]; //数据
}cache[CACHE_SIZE_L1/CACHE_BLOCK_SIZE];//每个缓存块的大小
struct SecondaryCache{
    bool valid,dirty;
    int tag;
    uint8_t data[CACHE_BLOCK_SIZE];
}cache2[CACHE_SIZE_L2/CACHE_BLOCK_SIZE];
void init_cache()
{
    int i;
    for (i = 0; i < CACHE_SIZE_L1 / CACHE_BLOCK_SIZE; i++)
    {
        cache[i].valid = false;
        cache[i].tag = 0;
        memset(cache[i].data, 0, CACHE_BLOCK_SIZE);
    }
    for (i = 0; i < CACHE_SIZE_L2 / CACHE_BLOCK_SIZE; i++)
    {
        cache2[i].valid = false;
        cache2[i].dirty = false;
        cache2[i].tag = 0;
        memset(cache2[i].data, 0, CACHE_BLOCK_SIZE);
    }
    tol_time = 0;
}//初始化
uint32_t cache_read(hwaddr_t addr)
{
    uint32_t gid = (addr >> 6) & 0x7f; //取地址的 6 到 12 位作为组号
    bool hit = false;
    int i;
    for (i = gid * EIGHT_WAY; i < (gid + 1) * EIGHT_WAY; i++)
    {//遍历该组中的所有缓存块（一级缓存采用 8 路组相联，即每组有 8 个缓存块）
        if (cache[i].tag == (addr >> 13) && cache[i].valid)
        {//检查每个缓存块的有效位 valid 和标签 tag，找到则命中
            hit = true; 
            tol_time += 2;
            break;
        }
    }
     if (!hit)
    { //未命中
        int j = second_cache_read(addr);//尝试从二级缓存中获取数据，并将返回的二级缓存块索引存储在变量 j 中
        for (i = gid * EIGHT_WAY; i < (gid + 1) * EIGHT_WAY; i++)
        {
            if (!cache[i].valid)
                break; //find cache[i].valid=0;
        }//再次遍历一级缓存该组的所有块，寻找一个无效的块以便加载数据
        if (i == (gid + 1) * EIGHT_WAY)//若所有块都是有效的
        {
            //random replacement algorism
            srand(0);
            i = gid * EIGHT_WAY + rand() % EIGHT_WAY;//随机方式的替换算法
        }
         cache[i].valid = true;
        cache[i].tag = addr >> 13;//更新缓存块
        memcpy(cache[i].data, cache2[j].data, CACHE_BLOCK_SIZE);
    }
    return i;
}
//二级缓存与一级基本相似
uint32_t second_cache_read(hwaddr_t addr)
{
    uint32_t gid = (addr >> 6) & ((1 << 12) - 1); 
    uint32_t block = (addr >> 6) << 6;
    int i;
    bool hit = false;
    for (i = gid * SIXTEEN_WAY; i < (gid + 1) * SIXTEEN_WAY; i++)
    {
        if (cache2[i].tag == (addr >> 18) && cache2[i].valid)
        {
            
            hit = true;
            
            tol_time += 20;
            break;
        }
    }
    if (!hit)
    {
        
        int j;
        for (i = gid * SIXTEEN_WAY; i < (gid + 1) * SIXTEEN_WAY; i++)
        {
            if (!cache2[i].valid)
                break;
        }
        if (i == (gid + 1) * SIXTEEN_WAY)
        {
            srand(0);
            i = gid * SIXTEEN_WAY + rand() % SIXTEEN_WAY;
            if (cache2[i].dirty)
            {
                uint8_t mask[BURST_LEN * 2];
                memset(mask, 1, BURST_LEN * 2);
                for (j = 0; j < CACHE_BLOCK_SIZE / BURST_LEN; j++)
                {
                    call_ddr3_write(block + j * BURST_LEN, cache2[i].data + j * BURST_LEN, mask);
                }
            }
        }
        cache2[i].valid = true;
        cache2[i].tag = addr >> 18;
        cache2[i].dirty = false;
        for (j = 0; j < BURST_LEN; j++)
        {
            call_ddr3_read(block + j * BURST_LEN, cache2[i].data + j * BURST_LEN);
        }
        tol_time += 200;
    }
    return i;
}

void cache_write(hwaddr_t addr, size_t len, uint32_t data)
{
    uint32_t gid = (addr >> 6) & 0x7f;
    uint32_t offset = addr & (CACHE_BLOCK_SIZE - 1); //计算偏移量
    int i;
    bool hit = false;
    for (i = gid * EIGHT_WAY; i < (gid + 1) * EIGHT_WAY; i++)//遍历缓存块
    {
        if (cache[i].tag == (addr >> 13) && cache[i].valid)
        {
            hit = true;
            break;
        }
    }
    //如果命中，使用memcpy将数据从地址&data复制到缓存块数据区的相应偏移位置 cache[i].data + offset
    if (hit)
    {
        memcpy(cache[i].data + offset, &data, len);
    }
    second_cache_write(addr, len, data);
}

void second_cache_write(hwaddr_t addr, size_t len, uint32_t data)
{
    uint32_t gid = (addr >> 6) & ((1 << 12) - 1);
    uint32_t offset = addr & (CACHE_BLOCK_SIZE - 1);
    bool hit = false;
    int i;
    for (i = gid * SIXTEEN_WAY; i < (gid + 1) * SIXTEEN_WAY; i++)
    {
        if (cache2[i].tag == (addr >> 13) && cache2[i].valid)
        {
            hit = true;
            break;
        }
    }
    if (!hit)
    {
        i = second_cache_read(addr);
    }
    cache2[i].dirty = true;
    memcpy(cache2[i].data + offset, &data, len);
}
uint32_t hwaddr_read(hwaddr_t addr, size_t len) {

	int index = is_mmio(addr);
	if (index >= 0)
	{
		return mmio_read(addr, len, index);
	}
	uint32_t offset = addr & (CACHE_BLOCK_SIZE - 1);
	uint32_t block = cache_read(addr);
	uint8_t temp[4];
	memset(temp, 0, sizeof(temp));
	if (offset + len >= CACHE_BLOCK_SIZE) //检查数据读取是否会跨越缓存块边界
	{
		uint32_t _block = cache_read(addr + len);
		memcpy(temp, cache[block].data + offset, CACHE_BLOCK_SIZE - offset);
		memcpy(temp + CACHE_BLOCK_SIZE - offset, cache[_block].data, len - (CACHE_BLOCK_SIZE - offset));
	}
	else
	{
		memcpy(temp, cache[block].data + offset, len);
	}
	int zero = 0;
	uint32_t cnt = unalign_rw(temp + zero, 4) & (~0u >> ((4 - len) << 3));

	return cnt;}




void hwaddr_write(hwaddr_t addr, size_t len, uint32_t data) {
	int index = is_mmio(addr);
	if (index >= 0)
	{
		mmio_write(addr, len, data, index);
		return;
	}
	cache_write(addr, len, data);
}

uint32_t lnaddr_read(lnaddr_t addr, size_t len) {
	return hwaddr_read(addr, len);
}

void lnaddr_write(lnaddr_t addr, size_t len, uint32_t data) {
	hwaddr_write(addr, len, data);
}

uint32_t swaddr_read(swaddr_t addr, size_t len) {
#ifdef DEBUG
	assert(len == 1 || len == 2 || len == 4);
#endif
	return lnaddr_read(addr, len);
}

void swaddr_write(swaddr_t addr, size_t len, uint32_t data) {
#ifdef DEBUG
	assert(len == 1 || len == 2 || len == 4);
#endif
	lnaddr_write(addr, len, data);
}